/* Copyright 2001,2002,2003 NAH6 BV
 * All Rights Reserved
 *
 *  $Header: /var/lib/cvs/secphone/ui/rng/fortuna/fnaPRNG.cpp,v 1.6 2003/11/21 16:12:58 itsme Exp $
 *
 *
 * Design of the PRNG is literally taken from 'Practical Cryptography', by Niels Ferguson, and Bruce Schneier.
 *
 */


#include "CPhone.h"

#include <windows.h>

#include "fnaPRNG.h"
#include "debug.h"


//---------------------------------------------------------------------------
PRNG::PRNG()
{
}
PRNG::~PRNG()
{
    for (size_t i=0 ; i<m_sources.size() ; i++)
        delete m_sources[i];
    m_sources.clear();
}

void PRNG::AddSource(EntropySource* src)
{
    src->SetParams(m_sources.size(), m_accu);

    m_sources.push_back(src);
    m_sourceevents.push_back(src->GetEvent());
}

DWORD PRNG::ThreadProc()
{
    DWORD tLastUpdate= 0;

    while(true)
    {
        int n= WAIT_TIMEOUT;
        if (m_sourceevents.size())
        {
            n= WaitForMultipleObjects(m_sourceevents.size(), vectorptr(m_sourceevents), FALSE, PRNG_POLL_INTERVAL);
        }
        else
        {
            Sleep(PRNG_POLL_INTERVAL);
        }

        if (n>=WAIT_OBJECT_0 && n<WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
        {
            ResetEvent(m_sourceevents[n-WAIT_OBJECT_0]);

            EntropySource* src= m_sources[n-WAIT_OBJECT_0];
            //debug("PRNG::ThreadProc: handling source %d (%hs)\n", n-WAIT_OBJECT_0, src->name());
            src->HandleSource();
        }

        if (n==WAIT_FAILED)
        {
            error("PRNG::ThreadProc WaitForMultipleObjects");
            break;
        }

        // first run, or was the last update more than 5 minues ago?
        if (tLastUpdate==0 || GetTickCount()-tLastUpdate > 300000)
        {
            debug("PRNG::ThreadProc - updating seedfile\n");
            if (!m_accu.UpdateSeedFile())
            {
                debug("running reseed process\n");
                theApp.RunReseedApp();

                m_accu.DoReseed();
                m_accu.WriteSeedFile();
            }
            tLastUpdate= GetTickCount();
        }

        // check all polled sources
        for (size_t i=0 ; i<m_sources.size() ; ++i)
        {
            if (m_sources[i]->IsPolledSource())
            {
                //debug("polling source %d (%hs)\n", i, m_sources[i]->name());
                m_sources[i]->HandleSource();
            }
        }
    }


    return 0;
}

bool PRNG::RandomData(int nBytesRequested, ByteVector& r)
{
    debug("PRNG::RandomData(%d)\n", nBytesRequested);
    return m_accu.RandomData(nBytesRequested, r);
}

